﻿//----------------------------------------------------------------------------
// Copyright (c) Pavel Tishkin 2012-2013.
//
// You must not remove this notice, or any other, from this software.
//----------------------------------------------------------------------------

namespace Lib.Dxp.Core


module Reflect = 

    open System
    open System.Reflection
    open System.Linq
    open System.Linq.Expressions



    let GetValueGetter<'T>(propertyInfo : PropertyInfo) : Func<'T, obj> =
        //if typeof<'T> <> propertyInfo.DeclaringType then raise <| ArgumentException()
        let instance = Expression.Parameter(propertyInfo.DeclaringType, "i")
        let property = Expression.Property(instance, propertyInfo)
        let convert  = Expression.TypeAs(property, typeof<obj>)
        Expression.Lambda(convert, instance).Compile() :?> Func<'T, obj>

    let GetValueGetterX<'T, 'A>(propertyInfo : PropertyInfo) : Func<'T, 'A> =
        //if typeof<'T> <> propertyInfo.DeclaringType then raise <| ArgumentException()
        let instance = Expression.Parameter(propertyInfo.DeclaringType, "i")
        let property = Expression.Property(instance, propertyInfo)
        let convert  = Expression.TypeAs(property, typeof<'A>)
        Expression.Lambda(convert, instance).Compile() :?> Func<'T, 'A>

    let GetPropertyValue(pi : PropertyInfo, objectX : obj) = 
        GetValueGetterX(pi).Invoke(objectX)

    let GetValueSetter<'T>(propertyInfo : PropertyInfo) : Action<'T, obj>  =
        //if typeof<'T> <> propertyInfo.DeclaringType then raise <| ArgumentException()
        let instance = Expression.Parameter(propertyInfo.DeclaringType, "i")
        let argument = Expression.Parameter(typeof<obj>, "a")
        let setterCall = Expression.Call(instance, propertyInfo.GetSetMethod(), Expression.Convert(argument, propertyInfo.PropertyType))
        Expression.Lambda(setterCall, instance, argument).Compile() :?> Action<'T, obj>



    /// Construct accessors from type information
    type Properties<'T>() =
        let _propGetters = new System.Collections.Generic.Dictionary<string, _>()
        let _propSetters = new System.Collections.Generic.Dictionary<string, _>()
        do 
             //let _type = match typed with null -> typeof<'T> | _ -> typed
             let _type = typeof<'T> 
             for pi in _type.GetProperties(BindingFlags.Public ||| BindingFlags.GetProperty
                                                               ||| BindingFlags.Instance) do
                if pi.CanRead  then _propGetters.Add(pi.Name, GetValueGetter(pi))
                if pi.CanWrite then _propSetters.Add(pi.Name, GetValueSetter(pi))

        /// Construct accessors from object
        new (i : ^T) = Properties< ^T >()

        member x.get      (propName : string) (obj : 'T) : 'a                = _propGetters.[propName].Invoke obj |> unbox 
        //member x.getBoxed (propName : string) (obj : 'T)                = _propGetters.[propName].Invoke obj  

        member x.set      (propName : string) (obj : 'T) (value : 'a)   = _propSetters.[propName].Invoke(obj, value)
        //member x.setBoxed (propName : string) (obj : 'T) (value : obj)  = _propSetters.[propName].Invoke(obj, value)

        member x.Names      with get () = _propGetters.Keys |> Seq.toArray
        member x.TypeName   with get () = typeof<'T>.Name